<template>
  <div
    ref="containerRef"
    style="position: relative"
  >
    <slot></slot>
  </div>
</template>

<script setup>
import { addUnit } from '@/app/style'
import { fillTexts, isRendering, rotateWatermark, getStyleStr, getMarkSize, getPixelRatio } from './utils'
import useMutationObserver from '@/hooks/useMutationObserver'
import watermarkProps from './props'

const props = defineProps(watermarkProps)

const containerRef = shallowRef()
const watermarkRef = shallowRef()
const stopObservation = ref(false)
const bodyRef = document.body
const FONT_GAP = 3

const gapX = computed(() => props.gap?.[0] ?? 100)
const gapY = computed(() => props.gap?.[1] ?? 100)
const gapXCenter = computed(() => gapX.value / 2)
const gapYCenter = computed(() => gapY.value / 2)
const offsetLeft = computed(() => props.offset?.[0] ?? gapXCenter.value)
const offsetTop = computed(() => props.offset?.[1] ?? gapYCenter.value)
const BaseSize = computed(() => {
  // 画布布局，1用于平行布局，2用于交替布局
  const layoutMap = {
    parallel: 1,
    alternate: 2,
  }
  return layoutMap[props.layout]
})
const markStyle = computed(() => {
  const markStyle = {
    zIndex: props.zIndex ?? 9,
    position: 'absolute',
    left: 0,
    top: 0,
    width: '100%',
    height: '100%',
    pointerEvents: 'none',
    backgroundRepeat: 'repeat',
  }
  // 计算偏移样式
  let positionLeft = offsetLeft.value - gapXCenter.value
  let positionTop = offsetTop.value - gapYCenter.value
  if (positionLeft > 0) {
    markStyle.left = addUnit(positionLeft)`${positionLeft}`
    markStyle.width = `calc(100% - ${addUnit(positionLeft)})`
    positionLeft = 0
  }
  if (positionTop > 0) {
    markStyle.top = addUnit(positionTop)
    markStyle.height = `calc(100% - ${addUnit(positionTop)})`
    positionTop = 0
  }
  markStyle.backgroundPosition = `${addUnit(positionLeft)} ${addUnit(positionTop)}`

  return markStyle
})
watch(
  () => props,
  () => props.auto && renderWatermark(),
  {
    deep: true,
    flush: 'post', // 在侦听器回调中访问被 Vue 更新之后的 DOM
  },
)

onMounted(() => props.auto && renderWatermark())
onBeforeUnmount(() => clear())

// 防止用户修改/隐藏水印
useMutationObserver(props.fullscreen ? bodyRef : containerRef, onMutate, {
  subtree: true, // 监听以 target 为根节点的整个子树
  childList: true, // 监听 target 节点中发生的节点的新增与删除
  attributes: true, // 观察所有监听的节点属性值的变化
  attributeFilter: ['style', 'class'], // 声明哪些属性名会被监听的数组。如果不声明该属性，所有属性的变化都将触发通知。
})

function onMutate(mutations) {
  if (!stopObservation.value) {
    mutations.forEach((mutation) => {
      if (isRendering(mutation, watermarkRef.value)) {
        clear()
        renderWatermark()
      }
    })
  }
}

function clear() {
  const watermarkRefVal = watermarkRef.value
  if (watermarkRefVal) {
    watermarkRefVal.remove()
    watermarkRef.value = undefined
  }
}

function appendWatermark(base64Url, markWidth) {
  const containerEl = containerRef.value
  const watermarkEl = watermarkRef.value
  if (containerEl && watermarkEl) {
    stopObservation.value = true
    watermarkEl.setAttribute(
      'style',
      getStyleStr({
        ...markStyle.value,
        backgroundImage: `url('${base64Url}')`,
        backgroundSize: addUnit((gapX.value + markWidth) * BaseSize.value),
      }),
    )
    if (props.fullscreen) {
      bodyRef.setAttribute('style', 'position: relative')
      bodyRef.append(watermarkEl)
    } else {
      containerEl?.append(watermarkEl)
    }

    setTimeout(() => {
      stopObservation.value = false
    })
  }
}

function renderWatermark() {
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')
  const p = props
  const image = p.image
  const rotate = p.rotate ?? -22
  if (ctx) {
    if (!watermarkRef.value) {
      watermarkRef.value = document.createElement('div')
    }
    const ratio = getPixelRatio()
    const [markWidth, markHeight] = getMarkSize(ctx, p, FONT_GAP)
    const canvasWidth = (gapX.value + markWidth) * ratio
    const canvasHeight = (gapY.value + markHeight) * ratio
    canvas.setAttribute('width', addUnit(canvasWidth * BaseSize.value))
    canvas.setAttribute('height', addUnit(canvasHeight * BaseSize.value))

    const drawX = (gapX.value * ratio) / 2
    const drawY = (gapY.value * ratio) / 2
    const drawWidth = markWidth * ratio
    const drawHeight = markHeight * ratio
    const rotateX = (drawWidth + gapX.value * ratio) / 2
    const rotateY = (drawHeight + gapY.value * ratio) / 2

    const alternateDrawX = drawX + canvasWidth
    const alternateDrawY = drawY + canvasHeight
    const alternateRotateX = rotateX + canvasWidth
    const alternateRotateY = rotateY + canvasHeight
    ctx.save()
    rotateWatermark(ctx, rotateX, rotateY, rotate)
    if (image) {
      const img = new Image()
      img.onload = () => {
        ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight)
        ctx.restore()
        rotateWatermark(ctx, alternateRotateX, alternateRotateY, rotate)
        ctx.drawImage(img, alternateDrawX, alternateDrawY, drawWidth, drawHeight)
        appendWatermark(canvas.toDataURL(), markWidth)
      }
      img.crossOrigin = 'anonymous'
      img.referrerPolicy = 'no-referrer'
      img.src = image
    } else {
      fillTexts(ctx, drawX, drawY, drawWidth, drawHeight, p, FONT_GAP)
      ctx.restore()
      rotateWatermark(ctx, alternateRotateX, alternateRotateY, rotate)
      fillTexts(ctx, alternateDrawX, alternateDrawY, drawWidth, drawHeight, p, FONT_GAP)
      appendWatermark(canvas.toDataURL(), markWidth)
    }
  }
}

defineExpose({
  renderWatermark,
  clear,
})
</script>
